import { SysMLUtilHelper } from 'src/api/sysml-imports';
import { modelApi } from 'src/api/model/modelApi';
//// import treeUtil from '.././ModelTree/treeUtil';
import G6 from '@antv/g6';
import { PropertyUtil } from 'src/components/Attribute/comps/PropertyUtil.ts';
import { GraphNodeUtil } from "pages/mxgraph/GraphNodeUtil";
const globalFontSize = 12;

//// 宽度配置
const STRINGCONFIG = {
  nameWidth: 90,  //// 需求名称
  textWidth: 160,   //// 内容
  typeWidth: 40,    //// 类型
};

const fittingString = (str, maxWidth, fontSize) => {
  const ellipsis = '...';
  const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
  let currentWidth = 0;
  let res = str;
  const pattern = new RegExp('[\u4E00-\u9FA5]+'); //// distinguish the Chinese charactors and letters
  str.split('').forEach((letter, i) => {
    if (currentWidth > maxWidth - ellipsisLength) return;
    if (pattern.test(letter)) {
      //// Chinese charactors
      currentWidth += fontSize;
    } else {
      //// get the width of single letter according to the fontSize
      currentWidth += G6.Util.getLetterWidth(letter, fontSize);
    }
    if (currentWidth > maxWidth - ellipsisLength) {
      res = `${str.substr(0, i)}${ellipsis}`;
    }
  });
  return res;
};

const register = function(columnIds){

  G6.registerNode(
    'customNode',
    {
      //// 绘制节点
      draw(cfg, group) {
        //// 如果 cfg 中定义了 style 需要同这里的属性进行融合
        let baseX = 0;
        let baseY = 0;
        if (columnIds.includes('comment')  && cfg.comment){
          group.addShape('image', {
            attrs: {
              x: 5,
              y: 0,
              img: 'statics/images/sysml/Comment.svg'
            },
            name: 'image-comment',
            draggable: true
          });
          baseX = 20;
        }
        const icon = group.addShape('image', {
          attrs: {
            x: 5 + baseX,
            y: 0,
            img: 'statics/images/sysml/Requirement.svg'
          },
          name: 'image-shape',
          draggable: true
        });
        baseX =  20 + icon.getBBox().maxX;
        if (columnIds.includes('id')){
          const id = group.addShape('text', {
            attrs: {
              x: baseX,
              y: 0,
              textBaseline: 'top',
              text: cfg.reqId,
              fill: 'rgba(17, 17, 17, 0.45)'
            },
            name: 'text-id',
            draggable: true
          });
          baseX =  5 + id.getBBox().maxX;
        }
        if (columnIds.includes('name')){
          const label = group.addShape('text', {
            attrs: {
              x: baseX,
              y: 0,
              textBaseline: 'top',
              text: cfg.fitName,
              fill: 'rgba(17, 17, 17, 0.85)',
              fontWeight: 500
            },
            name: 'text-shape',
            draggable: true
          });
          baseX = 5 + label.getBBox().maxX;
        }
        if (columnIds.includes('typeName')){
          const typelabel = group.addShape('text', {
            attrs: {
              x: baseX,
              y: 0,
              textBaseline: 'top',
              text: '<<需求>>',
              fill: 'rgba(17, 17, 17, 0.85)',
              fontWeight: 500
            },
            name: 'text-type',
            draggable: true
          });
          baseX = typelabel.getBBox().maxX;
          baseY = typelabel.getBBox().maxY;

        }
        if (columnIds.includes('content')){
          group.addShape('text', {
            attrs: {
              x: 5,
              y: 20,
              //// textAlign: 'center',
              textBaseline: 'top',
              text: cfg.fitText,
              fill: '#000'
            },
            name: 'text-content',
            draggable: true
          });
        }

        if (cfg.children?.length > 0){
          group.addShape('marker', {
            attrs: {
              x: baseX + 30,
              y: 20,
              r: 8,
              symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
              stroke: '#666',
              lineWidth: 2
            },
            draggable: true,
            name: 'collapse-icon'
          });
        }
        group.addShape('rect', {
          attrs: {
            x: 0,
            y: 0,
            height: baseY + 20,
            width: baseX + 20,
            stroke: '#ffff00',

          },
          visible: false,
          draggable: true,
          name: 'search-shape'
        });
        let cornerGroup = group.addGroup({ id: 'corner-group', draggable: true, visible: false });
        cornerGroup.addShape('image', {
          attrs: {
            x: baseX + 5,
            y: 14,
            width: 11,
            height: 11,
            img: 'statics/images/sidebar/add.svg'
          },
          draggable: true,
          name: 'add-icon'
        });
        cornerGroup.addShape('rect', {
          attrs: {
            x: baseX + 20,
            y: -5,
            height: 10,
            width: 10,
            fill: 'black',
            stroke: 'black',
          },
          name: 'corner1'
        });
        cornerGroup.addShape('rect', {
          attrs: {
            x: -5,
            y: baseY + 20,
            height: 10,
            width: 10,
            fill: 'black',
            stroke: 'black',
          },
          name: 'corner2'
        });
        cornerGroup.addShape('rect', {
          attrs: {
            x: baseX + 20,
            y: baseY + 20,
            height: 10,
            width: 10,
            fill: 'black',
            stroke: 'black',
          },
          name: 'corner3'
        });
        cornerGroup.addShape('rect', {
          attrs: {
            x: -5,
            y: -5,
            height: 10,
            width: 10,
            fill: 'black',
            stroke: 'black',
          },
          name: 'corner4'
        });

        return group;
      },
      setState(name, value, item) {
        const group = item.getContainer();
        let cornerGroup = group.findById('corner-group');
        if (name === 'selected') {
          if (value) {
            cornerGroup.show();
          } else {
            cornerGroup.hide();
          }
        }
      },

    },
    'rect'
  );
};
//// 注册自定义节点

//// 获取图的配置信息
/**
   * 可使用的方法
   *  diagramInfo.setLayoutDirection(SysMLConstants.LAYOUT_DIRECTION_VERTIVAL);
    diagramInfo.getLayoutDirection();

    //// 追溯层级 0..max
    diagramInfo.setDisplayDeepth(99);
    diagramInfo.getDisplayDeepth();

    //// 需求类型
    diagramInfo.setRequirementType("requirement", true);
    diagramInfo.getRequirementType("requirement");

    //// 范围 参数是元素的数组
    diagramInfo.setTraceBackRange(Array());
    diagramInfo.getTraceBackRange();

    //// 显示字段
    //// SysMLConstants.DISPLAY_CONTENT_NAME
    //// SysMLConstants.DISPLAY_CONTENT_REQUIREMENT_TYPE
    //// SysMLConstants.DISPLAY_CONTENT_REQUIREMENT_ID
    //// SysMLConstants.DISPLAY_CONTENT_COMMENT
    //// SysMLConstants.DISPLAY_CONTENT_BODY
    diagramInfo.setDisplayContent(SysMLConstants.DISPLAY_CONTENT_NAME, true);
    diagramInfo.getDisplayContent(SysMLConstants.DISPLAY_CONTENT_NAME);
   */
const getDiagramInfo = function(diagram){
  //// const diagramInfo = diagram.appliedStereotypes.toArray()
  ////   .filter(item => SysMLUtilHelper.isRequirementDerivationMap(item))[0]
  ////   .appliedStereotypes.toArray()
  ////   .filter(item => SysMLUtilHelper.isDiagramInfoStereotype(item))[0];
  //// return diagramInfo;
  //// return diagram.appliedStereotypes.toArray().filter(item => SysMLUtilHelper.isDiagramInfoStereotype(item))[0];
};

//// 获取线的配置信息
/**
   *
    //// 显示字段
    //// SysMLConstants.DISPLAY_CONTENT_NAME
    //// SysMLConstants.DISPLAY_CONTENT_REQUIREMENT_TYPE
    //// SysMLConstants.DISPLAY_CONTENT_REQUIREMENT_ID
    //// SysMLConstants.DISPLAY_CONTENT_COMMENT
    //// SysMLConstants.DISPLAY_CONTENT_BODY
    diagramInfo.setDisplayContent(SysMLConstants.DISPLAY_CONTENT_NAME, true);
    diagramInfo.getDisplayContent(SysMLConstants.DISPLAY_CONTENT_NAME);
    //// 追溯方向 SysMLConstants.TRACEBACK_DIRECTION_OUTGONING和SysMLConstants.TRACEBACK_DIRECTION_INCOMING表示出方向和入方向
    relationMap.getTraceBackDirection(SysMLConstants.TRACEBACK_DIRECTION_OUTGONING);
    relationMap.getTraceBackDirection();

    //// 关系类型
    //// SysMLConstants.RELATION_TYPE_TRACE
    //// SysMLConstants.RELATION_TYPE_REFINE
    //// SysMLConstants.RELATION_TYPE_DERIVE
    //// SysMLConstants.RELATION_TYPE_COPY
    //// SysMLConstants.RELATION_TYPE_CONTAINMENT
    relationMap.setRelationType(SysMLConstants.RELATION_TYPE_TRACE, true);
    relationMap.getRelationType(SysMLConstants.RELATION_TYPE_TRACE);

    //// 线的样式和颜色
    //// 第二个参数表示线的类型
    //// SysMLConstants.RELATION_STYLE_SOLID
    //// SysMLConstants.RELATION_STYLE_DASHED
    //// SysMLConstants.RELATION_STYLE_DOTTED
    //// 3、4、5个参数表示RGB
    relationMap.setRelationStyle(
      SysMLConstants.RELATION_TYPE_TRACE,
      SysMLConstants.RELATION_STYLE_SOLID,
      0,
      0,
      0
    );
    relationMap.getRelationStyle(SysMLConstants.RELATION_TYPE_TRACE).getLineStyle();
    relationMap.getRelationStyle(SysMLConstants.RELATION_TYPE_TRACE).getRed();
    relationMap.getRelationStyle(SysMLConstants.RELATION_TYPE_TRACE).getGreen();
    relationMap.getRelationStyle(SysMLConstants.RELATION_TYPE_TRACE).getBlue();
   */
const getRelationMap = function(diagram){
  return diagram.semanticModelBridge.elementCore.element;
};

export default {
  getDiagramInfo,
  getRelationMap,
  register,
  setProperty(diagram){
    const map =  diagram.getAppliedStereotypes().toArray()[0];
    const diagramInfo = map.getAppliedStereotypes().toArray()[0];
    const relationMap = map.getAppliedStereotypes().toArray()[1];

    //// 布局方向 SysMLConstants.LAYOUT_DIRECTION_VERTIVAL和SysMLConstants.LAYOUT_DIRECTION_HORIZONTAL表示布局横向和竖向
    //// diagramInfo.setLayoutDirection(SysMLConstants.LAYOUT_DIRECTION_VERTIVAL);
    //// diagramInfo.getLayoutDirection();
    relationMap.setLayoutDirection("HORIZONTAL");

    //// 追溯层级 0..max
    //// diagramInfo.setDisplayDeepth(99);
    //// diagramInfo.getDisplayDeepth();
    relationMap.setDisplayDepth(99);

    //// 需求类型
    //// diagramInfo.setRequirementType("requirement", true);
    //// diagramInfo.getRequirementType("requirement");

    //// 范围 参数是元素的数组
    //// diagramInfo.setTraceBackRange(Array());
    //// diagramInfo.getTraceBackRange();

  },

  /**
   * 初始化DI
   * @param {*} diagram
   * @param {*} req
   */
  initTraceData(diagram, req){
    //// let RequirementDerivationMap =  diagram.appliedStereotypes.toArray()
    ////   .filter(item => SysMLUtilHelper.isRequirementDerivationMap(item))[0];
    ////
    //// RequirementDerivationMap.getReqTraceBack(req.model);
    return SysMLUtilHelper.getReqTraceBack(diagram, req.model);
  },
  addNode({ typeName, graphNode, diagram }){
    try {
      //// SysMLUtilHelper.addNode(parentElement, typeName, 'Requirement');
      //// let traceMap = diagram.appliedStereotypes.toArray()[0];
      //// traceMap.addNode(parentElement, typeName, 'Requirement');
      let relationMap = this.getRelationMap(diagram);
      let direction  = relationMap.getTraceBackDirection();

      const createdNode = graphNode.addChildNode("Requirement", app.activeProjectId);
      const newTitle = GraphNodeUtil.getNewChildNodeName(graphNode, '未命名需求');
      createdNode.updateName(newTitle);
      if (direction === 'OUTGOING'){
        graphNode.addChildEdge(typeName, graphNode, createdNode, app.activeProjectId);
      } else {
        graphNode.addChildEdge(typeName, createdNode, graphNode, app.activeProjectId);
      }
      //// GraphNodeUtil.initProperty(createdNode);
      //// actions.getAction("openCreateElement").run(graphNode, { label: "需求", value: "Requirement"});
      //// actions.getAction()

    } catch (error) {
      console.log(error);
      throw error;
    }

    //// let modelElement = modelApi.createElementByTypeName({ typeName, parentElement, diagram });
    //// return modelElement;
  },
  deleteNode(){
    //// TODO
    modelApi.deleteModelElement();
  },
  removeNode({ shape, diagram }){
    //// let traceMap = diagram.appliedStereotypes.toArray()[0];
    //// traceMap.removeDIFromMapDiagram(shape);
    //// SysMLUtilHelper.removeDIFromMapDiagram(shape);
    let relationMap = this.getRelationMap(diagram);
    let deletedNodes = relationMap.getDeletedNodes().toArray();
    deletedNodes.push(shape.getId());
    relationMap.setDeletedNodes(deletedNodes);
    if (relationMap.getInitialModel() != null && relationMap.getDeletedNodes().toArray().includes(relationMap.getInitialModel().id)) {
      relationMap.setInitialModel(null);
      relationMap.setDeletedNodes([]);
    }
  },
  getRequirementRelationType(){
    return [
      {
        key: '1',
        name: '追踪',
        isVisible: true,
        icon: 'Trace',
        style: { color: '#5AD8A6' },
      },
      {
        key: '2',
        name: '精化',
        icon: 'Refine',
        isVisible: true,
        style: { color: '#F6BD16' },
      },
      {
        key: '3',
        name: '复制',
        icon: 'Copy',
        isVisible: false,
        style: { color: '#E8684A' },
      },
      {
        key: '4',
        name: '派生',
        isVisible: false,
        icon: 'DeriveReqt',
        style: { color: '#5D7092' },
      },
      {
        key: '5',
        name: '包含',
        isVisible: false,
        icon: 'Containment',
        style: { color: '#BDC6D0' },
      }
    ];
  },
  getContextMenu: function() {
    return  [
      { label: '属性设置', value: 'propertySetting', showIcon: true, icon: "statics/icons/app/iconproperty2.png", },
      { label: '树上高亮', value: 'modelTreeSelect', showIcon: true, },

      //// { label: '导航', value: 'goTo', children: goToChildren },
      { label: '移出', value: 'removeCell', icon: "statics/icons/table/removeFromTable.svg", showIcon: true, },
      { label: '删除', value: 'deleteCell', icon: "statics/icons/app/icondelete2.png", showIcon: true, }
    ];
  },
  getMenuChildren: function (value) {
    return [{
      key: '1',
      label: '需求',
      icon: 'statics/images/sysml/Requirement.svg',
      value,
      showIcon: true,
    }];
  },
  getAddMenuConfig: function() {
    return [
      {
        key: '1',
        label: '追踪',
        icon: 'statics/images/sysml/Trace.svg',
        //// value: 'Trace',
        showIcon: true,
        children: this.getMenuChildren('Trace')
      },
      {
        key: '2',
        label: '精化',
        icon: 'statics/images/sysml/Refine.svg',
        showIcon: true,
        value: 'Refine',
        children: this.getMenuChildren('Refine')
      },
      {
        key: '3',
        label: '复制',
        icon: 'statics/images/sysml/Copy.svg',
        showIcon: true,
        value: 'Copy',
        children: this.getMenuChildren('Copy')
      },
      {
        key: '4',
        label: '派生',
        icon: 'statics/images/sysml/DeriveReqt.svg',
        showIcon: true,
        value: 'DeriveReqt',
        children: this.getMenuChildren('DeriveReqt')
      }
      //// {
      ////   key: '5',
      ////   label: '包含',
      ////   icon: 'statics/images/sysml/Containment.svg',
      ////   showIcon: true,
      ////   value: 'Containment',
      ////   children: this.getMenuChildren('Containment')
      //// }
    ];
  },
  //// 超长文本适配
  fitStringData(data){
    data.forEach(node => {
      node.fitName = fittingString(node.name, STRINGCONFIG.nameWidth, globalFontSize);
      node.fitText = fittingString(node.text, STRINGCONFIG.textWidth, globalFontSize);
      if (node.children && node.children.length > 0 ){
        this.fitStringData(node.children);
      }
    });

    return data;
  },
  initMap(data){
    const container = this.$refs.mountNode;
    const width = container.scrollWidth;
    const legend = this.legend;
    const height = container.scrollHeight || 500;
    const defaultEdge =  {
      type: 'cubic-horizontal',
      style: {
        stroke: '#bcbcf2',
        lineWidth: 3,
        endArrow: this.traceDirection === 'OUTGOING',
        startArrow: this.traceDirection === 'INCOMING',
        //// ... 其他样式属性
      }
    };
    //// TODO
    let graph =  new G6.TreeGraph({
      container: this.$refs.mountNode,
      width,
      height,
      maxZoom: 5,
      minZoom: 0.2,
      modes: {
        default: [
          {
            type: 'collapse-expand',
            onChange: function onChange(item, collapsed) {
              const data = item.get('model');
              const icon = item.get('group').find(element => element.get('name') === 'collapse-icon');
              if (collapsed) {
                icon.attr('symbol', G6.Marker.expand);
              } else {
                icon.attr('symbol', G6.Marker.collapse);
              }
              data.collapsed = collapsed;
              return true;
            },
            shouldBegin: e => {
              return e.target.cfg.name === 'collapse-icon';

            }
          },
          {
            type: 'tooltip',
            formatText: (model) => {
              return this.currentText;
            },
            shouldBegin: e => {
              return !!this.currentText &&  this.currentText !== '';
            },
            offset: 30
          },
          {
            type: 'edge-tooltip',
            formatText(model) {
              const name = model.targetNode.getModel().relationType;
              return name;
            },
            offset: 30
          },
          'drag-canvas',
          //// 'zoom-canvas',
          //// 'drag-node',
          'click-select'
        ]
      },
      defaultNode: {
        type: 'customNode',
        size: [200, 20],
        anchorPoints: [
          [0, 0.5],
          [1, 0.7]
        ],
      },

      defaultEdge: defaultEdge,
      layout: {
        type: 'mindmap',
        direction: "V",
        getHeight: () => {
          return 80;
        },
        getWidth: () => {
          return 200;
        },
        getVGap: () => {
          return 10;
        },
        getHGap: () => {
          return 100;
        },
        getSide: () => {
          return 'right';
        }
      }
      //// animate: true, //// Boolean，切换布局时是否使用动画过度，默认为 false/
      //// animateCfg: {
      ////   duration: 200, //// Number，一次动画的时长
      ////   easing: 'linearEasing', //// String，动画函数
      //// },
    });
    graph.on('node:click', ev => {
      const shape = ev.target;
      const node = ev.item;
      this.selectedNode = node;
      let group = node.get('group');
      let cornerGroup = group.findById('corner-group');
      if (node.hasState('selected')){
        cornerGroup.show();
      } else {
        cornerGroup.hide();
      }

    });
    graph.on('canvas:click', ev => {
      console.log('canvas:click');
      this.selectedNode = null;

    });
    graph.on('itemcollapsed', ev => {
      this.graph.fitCenter();
    });
    graph.on('node:contextmenu', evt => {
      const node = evt.item;
      this.onContextMenu(node);
    });
    graph.on('node:mouseover',  (evt) => {
      const node = evt.item;
      const name = evt.target.cfg.name;
      const model = node.getModel();
      switch (name) {
        case 'text-shape':
          this.currentText = model.name;
          break;
        case 'text-content':
          this.currentText = model.text;
          break;
        case 'text-id':
          this.currentText = model.rid;
          break;
        //// case 'text-type':
        ////   this.currentText = model.id;
        ////   break;
        case 'image-comment':
          this.currentText = model.comment;
          break;
        default:
          this.currentText = '';
          break;
      }
    });

    graph.on('collapse-icon:click', (evt) => {
      const item = evt.item; //// 被操作的节点 item
      const target = evt.target; //// 被操作的具体图形
      const data = item.get('model');
      let collapsed = !data.collapsed;
      if (collapsed) {
        target.attr('symbol', G6.Marker.expand);
      } else {
        target.attr('symbol', G6.Marker.collapse);
      }
      data.collapsed = collapsed;

      evt.originalEvent.stopPropagation();
      evt.stopPropagation();
      graph.render();
      graph.fitCenter();
    });
    graph.on('add-icon:click', (evt) => {
      const sourceshape = evt.item.getModel().graphNode;
      this.onAddMenu(sourceshape);
      evt.originalEvent.stopPropagation();
      evt.stopPropagation();
    });
    graph.on('beforepaint', () => {
      const edges = graph.getEdges();
      let relationMap = getRelationMap(this.diagram);
      edges.forEach((edge) => {

        const targetNode = edge.get('targetNode');
        const type = targetNode.getModel().relationType;
        const lineTypeFormat = {
          'solid': [],
          'dashed': [10, 10],
          'dotted': [1, 1]
        };
        const lineObj = legend.find((item) => item.key === type);

        if (lineObj){

          relationMap.setRelationStyle(
            type,
            lineObj.style.border,
            lineObj.style.color
          );
          let visibleList = legend.filter((item) => item.isVisible === true);
          let visibleListIds = visibleList.map(item => item.key);
          relationMap.setRelationTypeVisibleByList(
            visibleListIds
          );
          graph.updateItem(edge, {
            style: {
              stroke: lineObj.style.color,
              lineDash: lineTypeFormat[lineObj.style.border]
            }
          });
          graph.refreshItem(edge);
        }

      });
    });
    graph.data(data);
    graph.render();
    graph.fitCenter();
    if (typeof window !== 'undefined')
      window.addEventListener('resize', () => {
        if (!graph) return;
        if (
          !container ||
          !container.scrollWidth ||
          !container.scrollHeight
        )
          return;
        graph.changeSize(container.scrollWidth, container.scrollHeight);
      });
    return graph;
  },

  /**
   *
   * @param {*} data
   * @param {*} DI
   * @param {*} direction
   * @return {Array}
   *
   */
  TreeDataFromDI(graphElement, diagram){

    let bfsDepth = 0;//// 遍历的深度
    let visitedEdges = [];//// 访问过的线
    let visitedNodes = [];//// 访问过的点

    /**
     * 判断graphElement的element的stereotype是否包含requirement
     * @param graphElement
     * @returns graphElement的element的stereotype是否包含requirement
     */
    function isRequirement(graphElement) {
      return SysMLUtilHelper.isRequirement(graphElement.semanticModelBridge.elementCore.element);
    }

    /**
     * 根据方向得到graphEdge一端的graphNode
     * @param graphEdge
     * @param diagram
     * @returns {GraphNode}
     */
    function getNeighborGraphNode(graphEdge, diagram) {
      if (diagram.getTraceBackDirection() === 'OUTGOING') {
        return graphEdge.anchorages.toArray()[1].graphElement;
      } else {
        return graphEdge.anchorages.toArray()[0].graphElement;
      }
    }

    /**
     * 返回graphElement邻接的符合条件的edge的集合
     * @param graphElement 起始graphNode
     * @param diagram 追溯图
     * @returns 邻接的edge的集合
     */
    function getNeighborRelations(graphElement, diagram) {
      /**
       * 标志各个线是否显示
       */
      let containmentVisible = diagram.getRelationTypeVisible('Containment') !== undefined && diagram.getRelationTypeVisible('Containment');
      let refineVisible = diagram.getRelationTypeVisible('Refine') !== undefined && diagram.getRelationTypeVisible('Refine');
      let traceVisible = diagram.getRelationTypeVisible('Trace') !== undefined && diagram.getRelationTypeVisible('Trace');
      let copyVisible = diagram.getRelationTypeVisible('Copy') !== undefined && diagram.getRelationTypeVisible('Copy');
      let deriveVisible = diagram.getRelationTypeVisible('DeriveReqt') !== undefined && diagram.getRelationTypeVisible('DeriveReqt');
      let neighbors = [];//// 用于容纳返回的graphEdge
      let edges = null;//// 要遍历的edge的集合
      let connectors = graphElement.getAnchorages();
      if (diagram.getTraceBackDirection() === 'OUTGOING') {
        edges = connectors.filter(item => item.graphEdge.getSourceConnector().getGraphElement() === graphElement);
      } else {
        edges = connectors.filter(item => item.graphEdge.getTargetConnector().getGraphElement() === graphElement);
      }

      edges.forEach(item => {
        if (getNeighborGraphNode(item.graphEdge, diagram).baseConfig.type_0 === "Requirement") {
          //// if (containmentVisible === true && item.graphEdge.baseConfig.type_0 === "Containment") {
          ////   neighbors.push(item.graphEdge);
          //// } else
          if (refineVisible === true && item.graphEdge.baseConfig.type_0 === "Refine") {
            neighbors.push(item.graphEdge);
          } else if (traceVisible === true && item.graphEdge.baseConfig.type_0 === "Trace") {
            neighbors.push(item.graphEdge);
          } else if (copyVisible === true && item.graphEdge.baseConfig.type_0 === "Copy") {
            neighbors.push(item.graphEdge);
          } else if (deriveVisible === true && item.graphEdge.baseConfig.type_0 === "DeriveReqt") {
            neighbors.push(item.graphEdge);
          }
        }
      });

      /**
       * 筛掉已经访问过的节点，删除掉的节点，和本身
       * @type {null}
       */
      let neighborsArray = null;
      if (neighbors.filter(item => !visitedEdges.includes(item.id)
        && !diagram.getDeletedNodes().toArray().includes(getNeighborGraphNode(item, diagram).id)
        && graphElement.id !== getNeighborGraphNode(item, diagram).id
      ).length === 0) {
        neighborsArray = [];
      } else {
        neighborsArray = neighbors.filter(item => !visitedEdges.includes(item.id)
          && !diagram.getDeletedNodes().toArray().includes(getNeighborGraphNode(item, diagram).id)
          && graphElement.id !== getNeighborGraphNode(item, diagram).id
        );
      }

      //// 为了Containment，特殊处理
      if (diagram.getTraceBackDirection() === 'OUTGOING') {
        if (containmentVisible === true) {
          graphElement.children.forEach(item => {
            if (item.baseConfig.type_0 === "Requirement") {
              if (!diagram.getDeletedNodes().toArray().includes(item.id) && !visitedNodes.includes(item.id)) {
                neighborsArray.push(item);
              }
            }
          });
        }
      }
      return neighborsArray;
    }

    /**
     * 访问一个节点，并创建其追溯子节点的对象
     * @param G6Model 用于G6框架显示的对象
     * @param neighbors 邻接的graphEdge
     * @param diagram
     * @returns {*}
     */
    function visiteNode(G6Model, neighbors, diagram) {
      let children = neighbors.map(
        neighbor => {
          let graphElement = null;
          let relationType = null;
          if (neighbor.getType() === "node") {
            visitedNodes.push(neighbor.id);
            graphElement = neighbor;
            relationType = "Containment";
          } else if (neighbor.getType() === "relation") {
            visitedEdges.push(neighbor.id);
            graphElement = getNeighborGraphNode(neighbor, diagram);
            relationType = neighbor.baseConfig.type_0;
          }
          let obj = {
            model: graphElement,
            rid: graphElement.id,
            name: graphElement.name || '',
            reqId: PropertyUtil.getNodePropertyValue(graphElement, "Id"),
            requirementType: "Requirement",
            sysmlType: undefined,
            text: PropertyUtil.getNodePropertyValue(graphElement, "Text"),
            comment: PropertyUtil.getNodePropertyValue(graphElement, "Comment") || '',
            relationType: relationType,
            graphNode: graphElement,
          };
          obj.searchKey = obj.name + obj.text + obj.comment + obj.reqId;
          return obj;
        });
      G6Model.children = children;
      return children;
    }

    /**
     * 根据模型得到一个用于G6框架显示的js对象
     * @param graphElement
     * @returns {{sysmlType: undefined, name, graphNode: {name}|*, model: *, comment, text: string | any, rid: *, requirementType: string, reqId: string | any}}
     */
    function getG6Object(graphElement) {
      let obj = {
        model: graphElement,
        rid: graphElement.id,
        name: graphElement.name || '',
        reqId: PropertyUtil.getNodePropertyValue(graphElement, "Id"),
        requirementType: "Requirement",
        sysmlType: undefined,
        text: PropertyUtil.getNodePropertyValue(graphElement, "Text"),
        comment: PropertyUtil.getNodePropertyValue(graphElement, "Comment") || '',
        graphNode: graphElement,
        //// searchKey: graphElement.name + requirement.text + requirement.comment + requirement.reqId
      };
      obj.searchKey = obj.name + obj.text + obj.comment + obj.reqId;
      return obj;
    }

    let Q = [];
    let last = null;

    /**
     * 追溯图的入口
     * @param graphElement
     * @param diagram
     * @returns {{sysmlType: undefined, name, graphNode: ({name}|*), model: *, comment, text: (string|*), rid: *, requirementType: string, reqId: (string|*)}}
     */
    function getReqTraceBack(graphElement, diagram) {
      visitedEdges = [];
      visitedNodes = [];
      if (diagram.getInitialModel() !== null && diagram.getInitialModel().id !== graphElement.id) {
        getRelationMap(diagram).setDeletedNodes([]);
        diagram.setInitialModel(null);
      }

      if (!isRequirement(graphElement)) {
        return;
      }
      diagram.setInitialModel(graphElement.semanticModelBridge.elementCore.element);
      bfsDepth = 1;

      let rootG6Object = getG6Object(graphElement);
      Q.push(rootG6Object);
      last = rootG6Object;

      while (Q.length !== 0) {
        if (bfsDepth >= diagram.getDisplayDepth()) {
          break;
        }
        let neighbors = getNeighborRelations(Q[0].model, diagram);
        let children = visiteNode(Q[0], neighbors, diagram);
        Q = Q.concat(children);
        if (last === Q[0]) {
          last = Q[Q.length - 1];
          bfsDepth = bfsDepth + 1;
        }
        Q.splice(0, 1);
      }
      return rootG6Object;
    }

    let relationMap = this.getRelationMap(diagram);
    if (relationMap.getDeletedNodes().toArray().includes(graphElement.id)) {
      return [];
    } else {
      let rootNode = getReqTraceBack(graphElement, relationMap);
      const fitData = this.fitStringData([rootNode])[0];
      return fitData;
    }
  }
};
